<template>
  <div class="file-upload">
    <div class="file-upload-container"
         @click.prevent="triggerUpload"
         v-bind="$attrs">
      <slot v-if="fileStatus === 'loading'"
            name="loading">
        <button class="btn btn-secondary"
                disabled>正在上传</button>
      </slot>
      <!-- (slot特殊性)子组件给父组件需要的值  :uploadedData="uploadedData-->
      <slot v-else-if="fileStatus === 'success'"
            name="uploaded"
            :uploadedData="uploadedData">
        <button class="btn btn-success"
                disabled>上传成功</button>
      </slot>

      <slot v-else-if="fileStatus === 'ready'"
            name="default">
        <button class="btn btn-primary">点击上传</button>
      </slot>
    </div>
    <input type="file"
           class="file-input d-none"
           ref="fileInput"
           @change="handleFileChange">
    <br>
  </div>
</template>

<script lang='ts'>
import axios from 'axios'
import { defineComponent, PropType, ref, watch } from 'vue'

type UploadStatus = 'ready' | 'success' | 'error' | 'loading'

//为组件添加一个可传入的参数为File的自定义函数
type CheckFunction = (file: File) => boolean

export default defineComponent({
  name: 'Uploader',
  props: {
    // 请求地址
    action: {
      type: String,
      required: true
    },
    // 上传时的自定义操作
    beforeUpload: {
      type: Function as PropType<CheckFunction>
    },
    // 编辑时传入的数据
    uploaded: {
      type: Object
    }
  },
  // 禁止根节点继承默认HTML属性
  inheritAttrs: false,
  emits: ['file-uploaded', 'file-uploaded-error', 'file-uploaded-delete'],
  setup(props, context) {
    // 初始化 若是编辑状态则置为success
    const fileStatus = ref<UploadStatus>(props.uploaded ? 'success' : 'ready')
    const uploadedData = ref(props.uploaded)
    const fileInput = ref<null | HTMLInputElement>(null)

    // 监视props传来的参数变化,若有新的值则赋值
    watch(() => props.uploaded, (newValue) => {
      if (newValue) {
        fileStatus.value = 'success'
        uploadedData.value = newValue
      } else {
        fileStatus.value = 'ready'
      }
    })

    const triggerUpload = () => {
      fileInput.value && fileInput.value.click()
    }


    //处理上传图片
    const handleFileChange = (e: Event) => {
      const currentTarget = e.target as HTMLInputElement
      if (currentTarget.files) {
        const files = Array.from(currentTarget.files)
        if (props.beforeUpload) {
          const result = props.beforeUpload(files[0])
          if (!result) {
            return
          }
        }
        fileStatus.value = 'loading'

        const formData = new FormData()
        formData.append('files', files[0])
        axios.post(props.action, formData, {
          headers: {
            'Content-type': 'multipart/form-data'
          }
        }).then(resp => {
          fileStatus.value = 'success'
          //这里拿到上传成功的图片数据传给父组件(slot特殊性)
          uploadedData.value = resp.data
          context.emit('file-uploaded', resp.data)
        }).catch((error) => {
          fileStatus.value = 'error'
          context.emit('file-uploaded-error', { error })
        }).finally(() => {
          if (fileInput.value) {
            fileInput.value.value = ''
          }
        })
      }
    }
    //删除
    const UploadedDelete = () => {
      uploadedData.value = undefined
      fileStatus.value = 'ready'
      context.emit('file-uploaded-delete', '删除成功')
    }

    return {
      fileInput,
      triggerUpload,
      fileStatus,
      handleFileChange,
      uploadedData,
      UploadedDelete
    }
  }
})
</script>

<style scoped>
</style>